<?php

namespace Syrius\Model;


/**
 * Il modello rappresenta una classe generica di un oggetto presente a database
 * @abstract
 * @package	Model
 * @author	Domenico Biancardi <domenico.biancardi@gmail.com>
 * @link	http://code.google.com/p/syrius/
 *
 */ abstract class Model {
	private $_fields;

	private $_relations;

	/**
	 * nome della tabella
	 */
	protected $_table;

	/**
	 * Array of error messages and types
	 */
	private $_errors;

	/**
	 * Query log
	 */
	private $_queryLog;

	private $_primaryKey;

	/**
	 * Array che contiene tutte le chiavi del modello
	 */
	private $_keys;

	public function __construct($connectionName = "default") {
		$this->_driver = new \Syrius\Adapter\DAO ($connectionName);
		if ($this->_table == "")
			throw new \Exception ("Table not defined");
		if (count($this->fields()) == 0)
			throw new \Exception ("Fields not defined");
	}

	/**
	 * Legge i fields di modello e genera gli oggetti collegati(
	 */
	public function fields() {
		if (count($this->_fields) == 0) {
			$fields = get_object_vars($this);
			foreach ($fields as $fieldName => $fieldOpts) {
				if (!is_array($fieldOpts))
					continue;
				$type = $fieldOpts ["type"];
				if (isset ($fieldOpts ["extra_type"])) {
					$type = "Relation_" . $fieldOpts ["extra_type"];
					$this->_relations [] = $fieldName;
				}
				$className = "\\Syrius\\Type\\" . ucfirst($type);
				$fieldOpts ["name"] = $fieldName;
				$this->_fields [$fieldName] = new $className ($fieldOpts, $fieldName, $this->source());
				if (method_exists($this, "get" . ucfirst($fieldName))) {
					$methodName = "get" . ucfirst($fieldName);

					//$this -> _fields[$fieldName]  ->setCustomGet ( get_class ( $this ) . "::" . $methodName );
				}
				if (method_exists($this, "set" . ucfirst($fieldName))) {
					$methodName = "set" . ucfirst($fieldName);

					//$this -> _fields[$fieldName]  ->setCustomSet ( get_class ( $this ) . "::" . $methodName );
				}
				if (isset ($fieldOpts ["primary"]) && ($fieldOpts ['primary'] == true)) {
					$this->_keys [$fieldName] = new \Syrius\Keys\Primary ($fieldName, $fieldOpts);
					$this->_primaryKey = $this->_keys [$fieldName];
				}
				$this->_fields [$fieldName]->setSource($this->_table);
			}
		}

		return $this->_fields;
	}

	public function find() {
	}

	/**
	 * Questa funzione gestisce un differente input. Nel caso di un valore singolo viene effettuato un WHERE [campo] = [valore], in caso di un
	 * array allora viene effettuata una query del tipo WHERE [campo] in ([valore_array])
	 */
	public function findPK() {
	}

	/**
	 * ritorna la chiave primaria
	 */
	public function getPrimaryKey() {
		return $this->_primaryKey;
	}

	/**
	 * Operazione che avvia il migrate del modello sul database
	 */
	public function migrate() {
		$this->_driver->migrate($this);
	}

	public function source() {
		return $this->_table;
	}

	/**
	 * Effettua l'inserimento di una nuova riga
	 * @param array $data
	 * @return int
	 */
	public function save($data = array()) {

		$sql = new \Syrius\Query\Insert ($this->_table, array_keys($data), array_values($data));
		$query = new \Syrius\Adapter\Query ($sql);
		$primary = $this->_primaryKey->getName();
		$primary = $primary [0];
        // se la primary key è un serial allora prendo il lastInsertId altrimenti ritorno il valore
        $fields = $this -> fields();
        if ($fields[$primary] -> getAttribute("serial")){
            // prendo il lastInsertId
		    return $query->getInsertId();
        }else{
            return $data[$primary];
        }
	}

	/**
	 * Effettua l'aggiornamento di un record, per trovare la riga esistente
	 * ci si basa sui dati contenuti in oldData
	 * @param array $data
	 * @param array $oldData
	 * @return int
	 */
	public function update($data = array(), $oldData = array()) {
		$primary = $this->_primaryKey->getName();
		$primary = $primary [0];
		$sql = new \Syrius\Query\Update ($this->_table, array_keys($data), array_values($data), $primary, $oldData [$primary]);
		$query = new \Syrius\Adapter\Query ($sql);
		return $oldData [$primary];

	}


	/**
	 * Analizza se esiste il record basandosi su oldData
	 * e se esiste effettua un'update, altrimenti effettua un insert
	 * @param array $data
	 * @param array $oldData
	 */
	public function saveOrUpdate($data = array(), $oldData = array()) {
		$primary = $this->_primaryKey->getName();
		$primary = $primary [0];
		$tmpData = null;
		foreach ($data as $k => $v) {
			if ($v != "")
				$tmpData [$k] = $v;
		}
		$data = $this->_toDbCondition($tmpData);
		if (isset ($oldData [$primary]) && $oldData [$primary] != "") {
			return $this->update($data, $oldData);
		} else {
			return $this->save($data);
		}
	}

	public function selectOne($conditions) {
		if (!is_array($conditions)) {
			$primary = $this->_primaryKey->getName();
			$primary = $primary [0];
			$conditions = array($primary => $conditions);
		}
		$conditions = $this->_toDbCondition($conditions);
		$sql = new \Syrius\Query\Select ($this);
		$sql->where($conditions);
		$data = new \Syrius\Adapter\Query ($sql);
		$result = new \Syrius\Adapter\Result ($data);
		$entity = $this->getEntity($result->fetchAssocArray());
		return $entity;
	}

	public function select($conditions = null) {
		if ($conditions instanceof \Syrius\Query\Query) {
			return $this->query($conditions);
		} else if (!is_array($conditions) && $conditions != null) {
			$conditions = array($this->_primaryKey->getName() => $conditions);
		}
		$conditions = $this->_toDbCondition($conditions);
		$sql = new \Syrius\Query\Select ($this);
		if ($conditions != null)
			$sql->where($conditions);
		return $sql;
	}

	public function query($query) {
		$data = new \Syrius\Adapter\Query ($query);
		$result = new \Syrius\Adapter\Result ($data);
		$entityList = new \Syrius\Model\Entity_List ($result, $this);
		return $entityList;
	}

	/**
	 * Ritorna una entità a partire dal modello
	 */
	public function getEntity(array $data = null) {
		return new \Syrius\Model\Entity ($data, $this);
	}

	public function customQuery($sql) {
		$data = new \Syrius\Adapter\Query ($sql);
		$result = new \Syrius\Adapter\Result ($data);
		$entityList = new Entity_List ($result, $this);
		return $entityList;
	}

	public function relations() {
		$obj = array();
		for ($i = 0; $i < count($this->_relations); $i++) {
			$obj [$this->_relations [$i]] = $this->_fields [$this->_relations [$i]];
		}
		return $obj;
	}

	public function _toDbCondition($conditions = null) {
		if ($conditions == null)
			return $conditions;
        // sistemazione delle conditions in accordo con il tipo
		foreach ($conditions as $k => $v) {
			$conditions [$k] = $this->_fields [$k]->_toDb($v);
		}
		return $conditions;
	}

}

class FunctionM extends Model {
	protected $_body;

	protected $_function;

	/**
	 * Operazione che avvia il migrate del modello sul database
	 */
	public function migrate() {
		$this->_driver->migrateFunction($this);
	}

	public function getBody() {
		return $this->_body;
	}

	public function getName() {
		return $this->_function;
	}

	public function __construct($connectionName = "default") {
		$this->_driver = new \Syrius\Adapter\DAO ($connectionName);
		if ($this->_function == "")
			throw new Exception ("Function name not defined");
	}

}

?>
